#include "keytree.h"

#include <QDebug>


void outputString(const std::string& str) {
    Logger::log(str);
}

void visit(const KeyNode& keyNode, const std::string& chainName, const bool isLeafNode,
           const OptionsDict& optionsDict) {

//    if (! isLeafNode && ! optionsDict.at(OUTPUT_ENTIRE_CHAIN_OPTION))
//        return;

    outputString("* [Chain " + chainName + "]");
    if (keyNode.isPrivate()) {
        KeyNode keyNodePub= keyNode.getPublic();
        outputString("  * ext pub:  " + toBase58Check(keyNodePub.extkey()));
        outputString("  * ext prv:  " + toBase58Check(keyNode.extkey()));
//        if (! optionsDict.at(VERBOSE_OPTION)) {
            outputString("  * priv key: " + keyNode.privkey());
            outputString("  * address:  " + keyNode.address());
//        } else {
            outputString("  * uncompressed priv key: " + keyNode.privkey(false));
            outputString("  * uncompressed pub key:  " + uchar_vector(keyNode.pubkeyUncompressed()).getHex());
            outputString("  * uncompressed address:  " + keyNode.address(false));

            outputString("  * compressed priv key: " + keyNode.privkey(true));
            outputString("  * compressed pub key:  " + uchar_vector(keyNode.pubkey()).getHex());
            outputString("  * compressed address:  " + keyNode.address(true));
//        }
    } else {
        outputString("  * ext pub:  " + toBase58Check(keyNode.extkey()));
//        if (! optionsDict.at(VERBOSE_OPTION)) {
            outputString("  * address:  " + keyNode.address());
//        } else {
            //outputString("  * uncompressed pub key:  " + uchar_vector(keyNode.pubkeyUncompressed()).getHex());
            //outputString("  * uncompressed address:  " + keyNode.address(false));

            outputString("  * compressed pub key:  " + uchar_vector(keyNode.pubkey()).getHex());
            outputString("  * compressed address:  " + keyNode.address(true));
//        }
    }
}

KeyNode traversePreorder(const KeyNode &keyNode, TreeChains treeChains, const std::string &chainName) {
    if (! treeChains.empty()) {
        IsPrivateNPathRange isPrivateNPathRange = treeChains.front();
        treeChains.pop_front();
        bool isPrivate = isPrivateNPathRange.first;
        Range range = isPrivateNPathRange.second;
        uint64_t min = range.first;
        uint64_t max = range.second;

        if (min == KeyTreeUtil::NODE_IDX_M_FLAG && max == KeyTreeUtil::NODE_IDX_M_FLAG) {
            KeyNode ret_node = traversePreorder(keyNode, treeChains, chainName);

            if (ret_node.privkey().empty()) {
                return keyNode;
            }
            else {
                return ret_node;
            }
        } else {
            for (uint64_t i = min; i <= max; ++i) {
                uint32_t k = (uint32_t)i;
                if (isPrivate) k = KeyTreeUtil::toPrime(k);
                std::string childChainName = chainName + "/" + KeyTreeUtil::iToString(k);
                KeyNode childNode = keyNode.getChild(k);

                KeyNode ret_node = traversePreorder(childNode, treeChains, childChainName);
                if (ret_node.privkey().empty()) {
                    return childNode;
                }
                else {
                    return ret_node;
                }

            }
        }
    }

    return {};
}




std::string outputExtKeysFromSeed(const std::string& seed, const std::string& chainStr/*,
                           StringUtils::StringFormat seedStringFormat, const int roundsToHash,
                           const OptionsDict& optionsDict, TreeTraversal::Type traversalType*/) {
    std::string seedHex;
//    if (seedStringFormat == StringUtils::ascii) {
        seedHex = StringUtils::string_to_hex(seed);
//    std::cout << seedHex.c_str() << std::endl;
//    } else if (seedStringFormat == StringUtils::hex) {
//        if (! StringUtils::isHex(seed))
//            throw std::runtime_error("Invalid hex string \"" + seed + "\"");

//        seedHex = seed;
//    } else throw std::runtime_error("Invalid seed string format.");

    uchar_vector seedBytes;
//    if (roundsToHash <= 0) {
        seedBytes = uchar_vector(seedHex);
//    } else {
//        uchar_vector hash = KeyTreeUtil::sha256Rounds(uchar_vector(seedHex), roundsToHash);
//        seedBytes = uchar_vector(hash);
//    }

//    if (optionsDict.find(TESTNET) != optionsDict.end()) {
//        if (optionsDict.at(TESTNET) == true) {
            KeyNode::setTestNet(true);
//        } else {
//            KeyNode::setTestNet(false);
//        }
//    } else {
//        KeyNode::setTestNet(false);
//    }

    KeyNodeSeed keyNodeSeed(seedBytes);
    bytes_t k = keyNodeSeed.getMasterKey();
    bytes_t c = keyNodeSeed.getMasterChainCode();
    KeyNode prv(k, c);
    TreeChains treeChains = KeyTreeUtil::parseChainString(chainStr, prv.isPrivate());

//    if (optionsDict.at(VERBOSE_OPTION))
//        outputString("Master (hex): " + seedBytes.getHex());

//    if (traversalType == TreeTraversal::postorder)
//        traversePostorder(prv, treeChains, KeyTreeUtil::MASTER_NODE_LOWERCASE_M);
//    else if (traversalType == TreeTraversal::levelorder) {
//        treeChains.pop_front();
//        std::deque<KeyNode> KeyNodeDeq;
//        std::deque<std::pair<uint64_t,std::string>> levelNChainDeq;
//        traverseLevelorder(prv, treeChains, KeyTreeUtil::MASTER_NODE_LOWERCASE_M, 0, KeyNodeDeq, levelNChainDeq, optionsDict);
//    }
//    else
//        traversePreorder(prv, treeChains, KeyTreeUtil::MASTER_NODE_LOWERCASE_M);

      KeyNode ret_key_node = traversePreorder(prv, treeChains, KeyTreeUtil::MASTER_NODE_LOWERCASE_M);
//      visit(ret_key_node,"",true,{});
      return ret_key_node.privkey();
}

std::string outputExtKeysFromExtKey(const std::string &extKey, const std::string &chainStr) {
    uchar_vector extendedKey(KeyTreeUtil::extKeyBase58OrHexToBytes(extKey));
    KeyNode keyNode(extendedKey);
    TreeChains treeChains = KeyTreeUtil::parseChainString(chainStr, keyNode.isPrivate());
    const KeyNode& ret_key_node = traversePreorder(keyNode, treeChains, KeyTreeUtil::LEAD_CHAIN_PATH);
    return ret_key_node.address();
}


std::string outputChildPrvKey(const std::string &extKey, const std::string &chainStr, bool isTest) {
    if (isTest) {
        KeyNode::setTestNet(true);
    } else {
        KeyNode::setTestNet(false);
    }

    uchar_vector extendedKey(KeyTreeUtil::extKeyBase58OrHexToBytes(extKey));
    KeyNode keyNode(extendedKey);
    TreeChains treeChains = KeyTreeUtil::parseChainString(chainStr, keyNode.isPrivate());
    const KeyNode& ret_key_node = traversePreorder(keyNode, treeChains, KeyTreeUtil::LEAD_CHAIN_PATH);

    KeyNode keyNodePub = ret_key_node.getPublic();
//    outputString("  * ext pub:  " + toBase58Check(keyNodePub.extkey()));
//    outputString("  * ext prv:  " + toBase58Check(keyNode.extkey()));
//    visit(keyNode,"0/0",true,{});
    return toBase58Check(keyNode.extkey());
}

int outputAddr(const std::string &prvKey, std::string &addr)
{
//    secp256k1_key key;
//    key.setPrivKey(std::vector<unsigned char>(prvKey.begin(),prvKey.end()));
//    std::cout << key.getPubKey();
}

std::string srOutputChildPrvKey(const std::string &extKey, const std::string &chainStr, bool isTest)
{
    if (isTest) {
        KeyNode::setTestNet(true);
    } else {
        KeyNode::setTestNet(false);
    }


    uchar_vector extendedKey(KeyTreeUtil::extKeyBase58OrHexToBytes(extKey));
    KeyNode keyNode(extendedKey);
    TreeChains treeChains = KeyTreeUtil::parseChainString(chainStr, keyNode.isPrivate());

    // 常规查找
    KeyNode ret_keyNode = traversePreorder(keyNode, treeChains, KeyTreeUtil::LEAD_CHAIN_PATH);
    return toBase58Check(ret_keyNode.extkey());
}

int srOutputKeyInfo(const std::string &ekPrvKey,
                    std::string &out_ekPubKey,
                    std::string &out_pubKey,
                    std::string &out_prvKey,
                    std::string &out_address,
                    bool isTest)
{
    if (isTest) {
        KeyNode::setTestNet(true);
    } else {
        KeyNode::setTestNet(false);
    }

    uchar_vector extendedKey(KeyTreeUtil::extKeyBase58OrHexToBytes(ekPrvKey));
    KeyNode keyNode(extendedKey);

//    visit(keyNode,"",true,{});

    out_ekPubKey = toBase58Check(keyNode.getPublic().extkey());
    out_pubKey = uchar_vector(keyNode.pubkey()).getHex();
    out_prvKey = keyNode.privkey();
    out_address = keyNode.address();

    return 0;
}

std::string srOutputExtKeysFromSeed(const std::string &seed,
                                    const std::string &chainStr,
                                    bool isTest)
{
    KeyNode::setTestNet(isTest);

    std::string seedHex;
    seedHex = StringUtils::string_to_hex(seed);
    uchar_vector seedBytes;
    seedBytes = uchar_vector(seedHex);

    KeyNodeSeed keyNodeSeed(seedBytes);
    bytes_t k = keyNodeSeed.getMasterKey();
    bytes_t c = keyNodeSeed.getMasterChainCode();
    KeyNode prv(k, c);
    TreeChains treeChains = KeyTreeUtil::parseChainString(chainStr, prv.isPrivate());

    KeyNode ret_key_node = traversePreorder(prv, treeChains, KeyTreeUtil::MASTER_NODE_LOWERCASE_M);

    return toBase58Check(ret_key_node.extkey());
}
